package com.twb.core.jackson;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.google.common.collect.Lists;

@Configuration
@ConditionalOnProperty(name = { "twb.jackson.enabled" }, havingValue = "true")
public class JacksonConfiguration implements WebMvcConfigurer {

	Logger logger = LoggerFactory.getLogger(getClass());
	
	@Autowired
	JacksonConfigProperties jacksonConfigProperties;
	
	@Bean(name = "mapperObject")
	public ObjectMapper getObjectMapper() {
	    ObjectMapper om = new ObjectMapper();
	    JavaTimeModule javaTimeModule = new JavaTimeModule();
	    javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(jacksonConfigProperties.getDateTimeFormat())));
	    javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(jacksonConfigProperties.getDateFormat())));
	    javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(jacksonConfigProperties.getTimeFormat())));
	    DateFormat dateFormat = new SimpleDateFormat(jacksonConfigProperties.getDateTimeFormat());
	    javaTimeModule.addSerializer(Date.class, new DateSerializer(true,dateFormat));
	    javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(jacksonConfigProperties.getDateTimeFormat())));
        javaTimeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern(jacksonConfigProperties.getDateFormat())));
        javaTimeModule.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern(jacksonConfigProperties.getTimeFormat())));

      //Date序列化和反序列化
        /*javaTimeModule.addSerializer(Date.class, new JsonSerializer<Date>() {
            @Override
            public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                SimpleDateFormat formatter = new SimpleDateFormat(jacksonConfigProperties.getDateTimeFormat());
                String formattedDate = formatter.format(date);
                jsonGenerator.writeString(formattedDate);
            }
        });*/
        javaTimeModule.addDeserializer(Date.class, new JsonDeserializer<Date>() {
            @Override
            public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
            	try {
            		List<String> FORMATS = Lists.newArrayList(
            		        "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyyMMdd-HHmmss", "yyyy-MM-dd", "MM-dd", "HH:mm:ss", "yyyy-MM"
            		    );
            		DateFormat df = null;
                    String dateValue = jsonParser.getText();
                    if (StringUtils.isEmpty(dateValue)) {
                        return null;
                    }
                    for (String p : FORMATS) {
						if(dateValue.length()==p.length()) {
							df = new SimpleDateFormat(p);
							break;
						}
					}
                    logger.info("使用自定义解析器解析字段:{}:时间:{}",jsonParser.getCurrentName(),jsonParser.getText());
                    Date date = null;
                    if (StringUtils.isNumeric(dateValue)){
                        date = new Date(Long.valueOf(dateValue));
                    }else {
                        String[] patterns = FORMATS.toArray(new String[0]);
                        date = DateUtils.parseDate(jsonParser.getText(),patterns);
                    }
                    return df.parse(df.format(date));
                } catch (Exception e) {
                    logger.error("JSON反序列化，时间解析失败", e);
                    throw new RuntimeException(e);
                }
            }
        });
	    om.registerModule(javaTimeModule);
	    // json进行换行缩进等操作
	    om.enable(SerializationFeature.INDENT_OUTPUT);
	    om.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
	    om.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
	    om.setTimeZone(TimeZone.getTimeZone(jacksonConfigProperties.getTimeZone()));
	    om.setDefaultPropertyInclusion(Include.valueOf(jacksonConfigProperties.getDefaultPropertyInclusion()));
	    om.setDateFormat(new SimpleDateFormat(jacksonConfigProperties.getDateTimeFormat()));
	    om.configure(SerializationFeature.INDENT_OUTPUT, jacksonConfigProperties.getIndentOutput());
	    //序列化的时候序列对象的所有属性
	    om.setSerializationInclusion(Include.ALWAYS);
	    //如果是空对象的时候,不抛异常
	    om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, jacksonConfigProperties.getFailOnEmptyBeans());
	    //反序列化的时候如果多了其他属性,不抛出异常
	    om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
	    return om;
	}
}
